﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Server
{
    class ServerApp : TSingleton<ServerApp>
    {
        Dictionary<IPEndPoint, UserSession> m_Users = new Dictionary<IPEndPoint, UserSession>();
        public UdpClient m_Server = new UdpClient(1234);
        Dictionary<ulong, Player> m_Players = new Dictionary<ulong, Player>();
        List<UserSession> m_TempUsers = new List<UserSession>();
        List<UserSession> m_MainThreadTempUsers = new List<UserSession>();
        public uint m_TickCount { private set; get; }
        uint m_PosSyncInterval = 3;

        public void Start()
        {
            //var t = Recv();
            //while (true)
            //{
            //    Thread.Sleep(1000);
            //    Console.WriteLine("tick ");
            //}
            //Console.WriteLine("end! ");
            //return;

            LogManager.Log("start server! ");

            CommonUtil.Init();
            OnRecvThread();
            OnSendThread();
            while (true)
            {
                Thread.Sleep(33);
                m_TickCount++;

                //lock (m_Users)
                {
                    m_MainThreadTempUsers.Clear();
                    m_MainThreadTempUsers.AddRange(m_Users.Values);
                    foreach (var item in m_MainThreadTempUsers)
                    {
                        item.Tick();
                    }
                }
            }
        }

        //async Task Recv()
        //{
        //    Console.WriteLine(await GetStringSync());
        //}

        //async Task<string> GetStringSync()
        //{
        //    var t = Task.Run(() =>
        //    {
        //        Thread.Sleep(1000);
        //        return "123";
        //    });
        //    return await t;
        //}

        ~ServerApp()
        {
            Stop();
        }

        public void Stop()
        {

        }

        public void StopUser(IPEndPoint ip)
        {
            if (m_Users.ContainsKey(ip))
            {
                var u = m_Users[ip];
                u.Stop();
                m_Users.Remove(ip);
            }
        }

        //private static void InitUdpClient()
        //{
        //    IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 5858);
        //    UdpClient udpClient = new UdpClient(RemoteIpEndPoint);
        //    //如果这里写while(true) 则会不停挂起异步接收操作，直到占满缓冲区间或队列。会报“由于系统缓冲区空间不足或队列已满，不能执行套接字上的操作”的错
        //    UdpState s = new UdpState(udpClient, RemoteIpEndPoint);
        //    udpClient.BeginReceive(EndReceive, s);
        //}

        //private static void EndReceive(IAsyncResult ar)
        //{
        //    try
        //    {
        //        UdpState s = ar.AsyncState as UdpState;
        //        if (s != null)
        //        {
        //            UdpClient udpClient = s.UdpClient;
        //            IPEndPoint ip = s.IP;
        //            Byte[] receiveBytes = udpClient.EndReceive(ar, ref ip);
        //            string msg = Encoding.UTF8.GetString(receiveBytes);
        //            udpClient.BeginReceive(EndReceive, s);//在这里重新开始一个异步接收，用于处理下一个网络请求
        //        }
        //    }
        //    catch (Exception ex) { }
        //}

        //public class UdpState
        //{
        //    public UdpClient UdpClient { get; } = null;
        //    public IPEndPoint IP { get; }
        //    public UdpState(UdpClient udpclient, IPEndPoint ip)
        //    {
        //        this.UdpClient = udpclient;
        //        this.IP = ip;
        //    }
        //}

        void OnRecvThread()
        {
            Task.Run(() =>
            {
                IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
                while (true)
                {
                    try
                    {
                        var bs = m_Server.Receive(ref ep);
                        //lock (m_Users)
                        {
                            if (!m_Users.ContainsKey(ep))
                            {
                                m_Users.Add(ep, new UserSession(ep));
                            }
                            m_Users[ep].ReceiveMessage(bs);
                        }
                    }
                    catch (Exception ex)
                    {
                        //LogManager.Log("ex=" + ex.Message);
                    }
                    //Thread.Sleep(33);
                }
            });
        }

        void OnSendThread()
        {
            Task.Run(() =>
            {
                while (true)
                {
                    //lock (m_Users)
                    {
                        m_TempUsers.Clear();
                        m_TempUsers.AddRange(m_Users.Values);
                        Player p;
                        foreach (var item in m_TempUsers)
                        {
                            foreach (var u in m_TempUsers)
                            {
                                p = GetPlayer(u.m_UserID);
                                if (p == null
                                    || (u.m_UserID % m_PosSyncInterval != m_TickCount % m_PosSyncInterval)
                                    )
                                {
                                    continue;
                                }
                                item.SendMessage(new S2C_PlayerSync { UserId = u.m_UserID, TickCount = m_TickCount, PosX = p.m_Pos.x, PosY = p.m_Pos.y, PosZ = p.m_Pos.z });
                            }
                        }
                    }
                    Thread.Sleep(33);
                }
            });
        }

        public void IterateUsers(Action<UserSession> action)
        {
            if (action == null)
            {
                return;
            }

            foreach (var item in m_Users)
            {
                action(item.Value);
            }
        }

        public Player GetPlayer(ulong userId)
        {
            if (m_Players.ContainsKey(userId))
            {
                return m_Players[userId];
            }
            return null;
        }

        public uint AddPlayer(uint userId)
        {
            var player = new Player(userId);
            m_Players.Add(userId, player);
            return player.m_PlayerID;
        }

        public void RemovePlayer(uint userId)
        {
            m_Players.Remove(userId);
        }
    }
}
